
/*! \file bmp085.c
    \brief This file contains all function implementations for the BMP085 API
    
    Details.
*/


/******************************************************************************
                   Includes section
******************************************************************************/
#include <bmp085.h>
#include <i2cPacket.h>
#include <appTimer.h>
#include <gpio.h>
#include <sensors.h>


#define USE_TMP175_TEMPERATURE
//#define DEBUG_BMP085
#undef DEBUG_BMP085

#ifdef DEBUG_BMP085

#include <string.h>
#include <stdio.h>
#include <usart.h>

unsigned char buf[50];
unsigned char uRx[50];
unsigned char uTx[50];
static HAL_UsartDescriptor_t uD =
{
	.tty = USART_CHANNEL_1,
	.mode = USART_MODE_ASYNC,
	.baudrate = USART_BAUDRATE_38400,
	.dataLength = USART_DATA8,
	.parity = USART_PARITY_NONE,
	.stopbits = USART_STOPBIT_1,
	.rxBuffer = uRx,
	.rxBufferLength = 50,
	.txBuffer = uTx,
	.txBufferLength = 50,
	.flowControl = USART_FLOW_CONTROL_NONE,
};

#endif // DEBUG_BMP085

/******************************************************************************
                   Define(s) section
******************************************************************************/
#define BMP085_DEVICE_ADDR	(0xEE >> 1)

#define BMP085_PROM_START_ADDR			0xAA
#define BMP085_PROM_DATA_LEN			22

#define BMP085_CTRL_MEAS_REGISTER		0xF4
#define BMP085_ADC_OUT_MSB_REGISTER		0xF6
#define BMP085_ADC_OUT_LSB_REGISTER		0xF7
#define BMP085_SOFT_RESET_REGISTER		0xE0

#define BMP085_T_MEASURE        0x2E				// temperature measurement
#define BMP085_P_MEASURE        0x34				// pressure measurement

/*
	Mode					Oversampling	# of Samples	Convertion Time [ms]
	ultra-low-power			0				1				4.5
	standard				1				2				7.5
	high resolution			2				4				13.5
	ultra-high-resolution	3				8				25.5
*/
#define BMP085_OVERSAMPLING					3	// oversampling setting

#define BMP085_DELAY						5	// in milliseconds
#define BMP085_MAX_DELAYS					5	// ( * DELAY) timeout value waiting for EOC

#define BMP085_STATE_QUEUE_LEN				12	// state queue length


/******************************************************************************
                   Types section
******************************************************************************/

BEGIN_PACK

// calibration structure
typedef struct {
	int16_t ac1;
    int16_t ac2;
    int16_t ac3;
    uint16_t ac4;
    uint16_t ac5;
    uint16_t ac6;
    int16_t b1;
    int16_t b2;
    int16_t mb;
    int16_t mc;
    int16_t md;	
} PACK Bmp085Calibration_t;

END_PACK

static uint16_t ut;
static uint32_t up = 0;
static uint8_t nr_delays;


/******************************************************************************
                   Global variables section
******************************************************************************/
// Contents of the PROM - calibration values
static Bmp085Calibration_t bmp085Calibration;

// hods a FIFO queue of task for the bspBMP085Handler task handler function
volatile Bmp085States_t bmp085State[BMP085_STATE_QUEUE_LEN] = { BMP085_IDLE };

// Final measurement results
static Bmp085Control_t bmp085Control;

// I2C access control variable
static HAL_I2cDescriptor_t i2cParam = 
{
	.tty = TWI_CHANNEL_0,
	.id = BMP085_DEVICE_ADDR,
	.clockRate = I2C_CLOCK_RATE_62
};

// Timer structure, used to make the delays needed for A2D conversion
static HAL_AppTimer_t bmp085Delay;


/******************************************************************************
                   Implementations section
******************************************************************************/

/**************************************************************************//**
\brief  Adds a new task to the task queue, FIFO style.

\param[in]
  task - the task to be queued.

\return
  BC_FAIL - if the queue if full, has more than BMP085_STATE_QUEUE_LEN tasks
  BC_SUCCESS - other case.

******************************************************************************/
result_t bmp085PushTask(Bmp085States_t task)
{	
	uint8_t i;
	for(i = 0; i < BMP085_STATE_QUEUE_LEN - 1; i++)
	{
		/* the queue is filled with IDLE tasks by default.
		The first IDLE task found will be replaced by the task we want to add. */
		if( bmp085State[i] == BMP085_IDLE )
		{
			bmp085State[i] = task;
			return BC_SUCCESS;
		}
	}
	return BC_FAIL;
}

/**************************************************************************//**
\brief	Returns the first task to be executed from the queue, FIFO style.

\return
	the task in queue to be executed.
******************************************************************************/
Bmp085States_t bmp085PopTask(void)
{	
	uint8_t i;
	Bmp085States_t task = bmp085State[0];
	
	for( i=0; i < BMP085_STATE_QUEUE_LEN-1; i++)
	{
		bmp085State[i] = bmp085State[i+1];
	}
	return task;
}


/**************************************************************************//**
\brief  Initializes the sensor and starts reads the calibration values.

\param[out]
  bmp085Result - pointer to a variable holding the result BC_SUCCESS/FAIL of
				 the whole operation.
\param[in]
  bmp085Pressure - pointer to a variable that will hold the pressure value.

\return
  BC_FAIL - if the sensor was open and running other tasks.
  BC_SUCCESS - other case.

******************************************************************************/
result_t openBmp085(void)
{
	if (BMP085_IDLE == bmp085State[0])
	{
		// EOC status pin
		GPIO_3_make_in();
		
		// exercise master clear
		GPIO_USART_DTR_make_out();
		GPIO_USART_DTR_clr();
		GPIO_USART_DTR_set();
		
		
		#ifdef DEBUG_BMP085
		HAL_OpenUsart(&uD);
		
		/*sprintf((char*)buf, "I");
		HAL_WriteUsart(&uD, buf, strlen((char*)buf));*/
		#endif
		
		return BC_SUCCESS;
	}
	return BC_FAIL;
}


/**************************************************************************//**
\brief  Performs the test if the component have completed request.

\return
  BC_FAIL - the previous request is not completed.
  BC_SUCCESS - otherwise.
******************************************************************************/
result_t closeBmp085(void)
{
  //if (BMP085_IDLE == bmp085State[0])
//	return BC_FAIL;
  
  GPIO_USART_DTR_clr();
  
  //HAL_CloseUsart(&uD);
  
  return BC_SUCCESS;
    
}

/**************************************************************************//**
\brief Handles the I2C interrupts.

\param[in]
  result - the result of the operation true - successful, false - failed.
******************************************************************************/
void bmp085I2CHandler(bool result)
{
	// close resource
	HAL_CloseI2cPacket(&i2cParam);

	if (false == result)
	{
		#ifdef DEBUG_BMP085
		sprintf((char*)buf, "!");
		HAL_WriteUsart(&uD, buf, strlen((char*) buf));
		#endif
    	/* If failed, return BC_FAIL as result and call the upper task handler
	       with the return state in bmp085Control.bmp085Return assigned from
	       sensor.c file */
		bmp085Control.bmp085Callback(false, 0);
	}
	else
	{
		#ifdef DEBUG_BMP085
		/*sprintf((char*)buf, "+");
		HAL_WriteUsart(&uD, buf, strlen((char*) buf));*/
		#endif
    	// If successful, return BC_SUCCESS and proceed to the next task
		bmp085Control.bmp085Result = true;
		bspPostTask(BSP_PRESSURE);
	}
}



/**************************************************************************//**
\brief Reads the pressure value.
******************************************************************************/
result_t readBmp085Data(int16_t temperature, void (*f)(bool result, uint32_t data))
{
	if (BMP085_IDLE != bmp085State[0])
		return BC_FAIL;

	if (!f)
		return BC_FAIL;
		
	bmp085Control.bmp085Callback = f;

	#ifdef USE_TMP175_TEMPERATURE
	bmp085Control.bmp085Temperature = temperature;
	#endif

	if (BC_FAIL == bmp085PushTask(BMP085_WAIT))
		return BC_FAIL;

	/*  up = 0 means that this is the first run
		so read calibration values to memory
		and fix data (swap bytes) */
	if (up == 0)
	{
		if (BC_FAIL == bmp085PushTask(BMP085_GET_CAL))
			return BC_FAIL;
		if (BC_FAIL == bmp085PushTask(BMP085_FORMAT_CAL_DATA))
			return BC_FAIL;
	}		

	#ifndef USE_TMP175_TEMPERATURE
	if (BC_FAIL == bmp085PushTask(BMP085_SET_READ_TO_TEMP))
		return BC_FAIL;
	if (BC_FAIL == bmp085PushTask(BMP085_WAIT))
		return BC_FAIL;
	if (BC_FAIL == bmp085PushTask(BMP085_GET_UTEMP))
		return BC_FAIL;
	#endif
	
	if (BC_FAIL == bmp085PushTask(BMP085_SET_READ_TO_PRESSURE))
		return BC_FAIL;
	if (BC_FAIL == bmp085PushTask(BMP085_WAIT))
		return BC_FAIL;
	if (BC_FAIL == bmp085PushTask(BMP085_GET_UPRESSURE))
		return BC_FAIL;
	if (BC_FAIL == bmp085PushTask(BMP085_DO_MATH))
		return BC_FAIL;
	bspPostTask(BSP_PRESSURE);
	return BC_SUCCESS;
}

void bspBMP085WaitHandler(void)
{
	// check EOC pin
	if ( (GPIO_3_read() == 1) || (nr_delays == BMP085_MAX_DELAYS) )
	{
		// if high, conversion has ended and reads can be made
		bspPostTask(BSP_PRESSURE);
	}
	else
	{
		// else, wait again
		nr_delays++;
		HAL_StartAppTimer(&bmp085Delay);
	}
	return;
}


/**************************************************************************//**
\brief BMP085 Task Handler.
******************************************************************************/
void bspBMP085Handler(void)
{
	Bmp085States_t task;
	task = bmp085PopTask();
	
	/* auxiliar variables for calcs */
	int32_t param_b5;
	int32_t x1,x2,x3,b3,b6;
	uint32_t b4,b7;
	// Auxiliary variable for i2c
	uint8_t ctrl_reg_data;

	if (task ==	BMP085_IDLE)
	{	// IDLE state, does nothing
		return;
	}

	if (task ==	BMP085_WAIT)
	{	// wait BMP085_DELAY milliseconds
		bmp085Delay.interval = BMP085_DELAY;					// Timer interval in milliseconds
		bmp085Delay.mode     = TIMER_ONE_SHOT_MODE;				// One shot mode (TIMER_REPEAT_MODE or TIMER_ONE_SHOT_MODE)
		bmp085Delay.callback = bspBMP085WaitHandler;			// Callback function for timer fire event
		nr_delays = 0;
		HAL_StartAppTimer(&bmp085Delay);
		return;
	} // WAIT

	if (task ==	BMP085_GET_CAL)
	{	// read the calibrations values stored in the PROM
		if (-1 == HAL_OpenI2cPacket(&i2cParam))
		{
			bmp085I2CHandler(false);
			return;
		}			
  
		i2cParam.data = (uint8_t*)(&bmp085Calibration);	// where to store the data
		i2cParam.length = BMP085_PROM_DATA_LEN;						// read BMP085_PROM_DATA_LEN bytes
		i2cParam.lengthAddr = HAL_ONE_BYTE_SIZE;					// address has one byte
		i2cParam.internalAddr = BMP085_PROM_START_ADDR;				// start reading from BMP085_PROM_START_ADDR
		i2cParam.f = bmp085I2CHandler;								// when finished call bmp085I2CHandler
		
		if (-1 == HAL_ReadI2cPacket(&i2cParam))
			bmp085I2CHandler(false);

		return;
	} // GET_CAL
	
	if (task == BMP085_FORMAT_CAL_DATA)
	{
		/* swap bytes in each variable */
		/* parameters ac1, ac2, ac3, ac4, ac5, ac6 */
		bmp085Calibration.ac1 = SWAP16(bmp085Calibration.ac1);
		bmp085Calibration.ac2 = SWAP16(bmp085Calibration.ac2);
		bmp085Calibration.ac3 = SWAP16(bmp085Calibration.ac3);
		bmp085Calibration.ac4 = SWAP16(bmp085Calibration.ac4);
		bmp085Calibration.ac5 = SWAP16(bmp085Calibration.ac5);
		bmp085Calibration.ac6 = SWAP16(bmp085Calibration.ac6);
  
		/* parameters b1, b2 */
		bmp085Calibration.b1 = SWAP16(bmp085Calibration.b1);
		bmp085Calibration.b2 = SWAP16(bmp085Calibration.b2);
  
		/*parameters MB,MC,MD*/
		bmp085Calibration.mb = SWAP16(bmp085Calibration.mb);
		bmp085Calibration.mc = SWAP16(bmp085Calibration.mc);
		bmp085Calibration.md = SWAP16(bmp085Calibration.md);

		#ifdef DEBUG_BMP085
		sprintf((char*)buf, "ac1=%d; b1=%d\r\n", bmp085Calibration.ac1, bmp085Calibration.b1);
		HAL_WriteUsart(&uD, buf, strlen((char*) buf));
		#endif
		
		bspPostTask(BSP_PRESSURE);
		return;
	} // FORMAT_CAL_DATA
	
	if (task ==	BMP085_SET_READ_TO_TEMP)
	{	
		#ifdef DEBUG_BMP085
		/*sprintf((char*)buf, "-RTmp-\r\n");
		HAL_WriteUsart(&uD, buf, strlen((char*) buf));*/
		#endif
		// Set configuration register for temperature reading
		if (-1 == HAL_OpenI2cPacket(&i2cParam))
		{
			bmp085I2CHandler(false);
			return;
		}
  
		ctrl_reg_data = BMP085_T_MEASURE;					// Write 0x2E
		i2cParam.data = &ctrl_reg_data;						// to configuration register 0xF4
		i2cParam.length = 1;
		i2cParam.lengthAddr = HAL_ONE_BYTE_SIZE;
		i2cParam.internalAddr = BMP085_CTRL_MEAS_REGISTER;
		i2cParam.f = bmp085I2CHandler;

		if (-1 == HAL_WriteI2cPacket(&i2cParam))
			bmp085I2CHandler(false);

		return;
	} // GET_TEMP
	

	if (task ==	BMP085_GET_UTEMP)
	{	// Read uncalibrated temperature
		#ifdef DEBUG_BMP085
		/*sprintf((char*)buf, "-GUT-\r\n");
		HAL_WriteUsart(&uD, buf, strlen((char*) buf));*/
		#endif
		if (-1 == HAL_OpenI2cPacket(&i2cParam))
		{
			bmp085I2CHandler(false);
			return;
		}			
  
		i2cParam.data = (uint8_t*)(&ut);
		i2cParam.length = 2;									// 16bit word
		i2cParam.lengthAddr = HAL_ONE_BYTE_SIZE;
		i2cParam.internalAddr = BMP085_ADC_OUT_MSB_REGISTER;
		i2cParam.f = bmp085I2CHandler;

		if (-1 == HAL_ReadI2cPacket(&i2cParam))
			bmp085I2CHandler(false);

		return;		
	} // GET_UTEMP		

	if (task == BMP085_SET_READ_TO_PRESSURE)
	{	// Set configuration register for pressure reading
		if (-1 == HAL_OpenI2cPacket(&i2cParam))
		{
			bmp085I2CHandler(false);
			return;
		}			
  
		ctrl_reg_data = BMP085_P_MEASURE + ((uint8_t)BMP085_OVERSAMPLING << 6);	// the value written depends on the oversampling set
		i2cParam.data = &ctrl_reg_data;
		i2cParam.length = 1;
		i2cParam.lengthAddr = HAL_ONE_BYTE_SIZE;
		i2cParam.internalAddr = BMP085_CTRL_MEAS_REGISTER;
		i2cParam.f = bmp085I2CHandler;

		if (-1 == HAL_WriteI2cPacket(&i2cParam))
			bmp085I2CHandler(false);

		return;
	} // GET_PRESSURE

	if (task == BMP085_GET_UPRESSURE)
	{	// Read uncalibrated pressure
		if (-1 == HAL_OpenI2cPacket(&i2cParam))
		{
			bmp085I2CHandler(false);
			return;
		}			
  
		i2cParam.data = (uint8_t*)(&up);
		i2cParam.length = 3;
		i2cParam.lengthAddr = HAL_ONE_BYTE_SIZE;
		i2cParam.internalAddr = BMP085_ADC_OUT_MSB_REGISTER;
		i2cParam.f = bmp085I2CHandler;

		if (-1 == HAL_ReadI2cPacket(&i2cParam))
			bmp085I2CHandler(false);

		return;		
	} // GET_UPRESSURE

	if (task ==	BMP085_DO_MATH)
	{	// Do math
		
		#ifdef USE_TMP175_TEMPERATURE
		bmp085Control.bmp085Temperature = (bmp085Control.bmp085Temperature * 10) >> 4;
		param_b5 = (bmp085Control.bmp085Temperature << 4) - 8;
		
		ut = 0xCACA;
		#else
		/* swap bytes in ut */
		ut = SWAP16(ut);
		
		// Calculate calibrated temperature and param_b5 for pressure calculation
		x1 = (((int32_t) ut - (int32_t) bmp085Calibration.ac6) * (int32_t) bmp085Calibration.ac5) >> 15;
		x2 = ((int32_t) bmp085Calibration.mc << 11) / (x1 + bmp085Calibration.md);
		param_b5 = x1 + x2;
		bmp085Control.bmp085Temperature = ((param_b5 + 8) >> 4);
		#endif
		
		up = SWAP32(up);
		up >>= (8 + (8-BMP085_OVERSAMPLING));
	
		#ifdef DEBUG_BMP085
		sprintf((char*)buf, "ut=%X; up=%X%X\r\n", ut, (unsigned int) (up>>16), (unsigned int) up);
		HAL_WriteUsart(&uD, buf, strlen((char*) buf));
		#endif


		b6 = param_b5 - 4000;
		
		//*****calculate B3************
		x1 = (b6*b6) >> 12;
		x1 *= bmp085Calibration.b2;
		x1 >>=11;

		x2 = (bmp085Calibration.ac2*b6);
		x2 >>=11;

		x3 = x1 +x2;

		b3 = (((((int32_t)bmp085Calibration.ac1 )*4 + x3) << BMP085_OVERSAMPLING) + 2) >> 2;

		//*****calculate B4************
		x1 = (bmp085Calibration.ac3* b6) >> 13;
		x2 = (bmp085Calibration.b1 * ((b6*b6) >> 12) ) >> 16;
		x3 = ((x1 + x2) + 2) >> 2;
		b4 = (bmp085Calibration.ac4 * (uint32_t) (x3 + 32768)) >> 15;
     
		b7 = ((uint32_t)(up - b3) * (50000>>BMP085_OVERSAMPLING));
		if (b7 < 0x80000000)
		{
			bmp085Control.bmp085Pressure = (b7 << 1) / b4;
		}
		else
		{
			bmp085Control.bmp085Pressure = (b7 / b4) << 1;
		}

		x1 = bmp085Control.bmp085Pressure >> 8;
		x1 *= x1;
		x1 = (x1 * 3038) >> 16;
		x2 = (-7357 * (bmp085Control.bmp085Pressure)) >> 16;
		bmp085Control.bmp085Pressure += (x1 + x2 + 3791) >> 4;	// pressure in Pa
		
		
		#ifdef DEBUG_BMP085
		sprintf((char*)buf, "t=%d\r\n", bmp085Control.bmp085Temperature);
		HAL_WriteUsart(&uD, buf, strlen((char*) buf));
		sprintf((char*)buf, "pHex=%x%x\r\n", (unsigned int) (bmp085Control.bmp085Pressure>>16), (unsigned int) bmp085Control.bmp085Pressure);
		HAL_WriteUsart(&uD, buf, strlen((char*) buf));
		#endif
		
		bmp085Control.bmp085Callback(bmp085Control.bmp085Result, bmp085Control.bmp085Pressure);
		return;
	} // FORMAT_PRESSURE_DATA
}	


// eof bmp085.c
